package com.learn.mvc.config;

import com.learn.mvc.interceptor.TestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 代替SpringMVC的配置文件：
 * 包含可配置的功能：
 * 1、扫描组件
 * 2、视图解析器 例如 ThymeleafViewResolver
 * 3、视图控制器 view-controller
 * 4、开放对静态资源的访问  <mvc:default-servlet-handler />
 * 5、开启mvc注解驱动  <mvc:annotation-driven/>
 * 6、文件上传解析器 例如 CommonsMultipartResolver
 * 7、异常处理  例如 SimpleMappingExceptionResolver
 * 8、拦截器  <mvc:interceptors>
 * 这些方法由spring按需调用
 */
@Configuration //将当前类标识为一个配置类
@ComponentScan("com.learn.mvc") //1、扫描组件
@EnableWebMvc //5、开启mvc注解驱动
public class SpringmvcConfig implements WebMvcConfigurer { // Java工程，创建的就是 ApplicationContext ; web工程，就是WebApplicationContext

    /**
     * 2、配置视图解析器
     * 配置生成模板解析器
     * thymeleaf视图配置比较麻烦
     * jsp中的InternalResourceViewResolver比较简单
     */

    // 2.1 配置解析视图的模板策略
    // 类型虽然不是 SpringResourceTemplateResolver，但是ITemplateResolver 也兼容
    @Bean // 加上该标签后，方法的返回值直接成为IOC容器中的一个bean。 本身在springmvc.xml中也是通过Bean引入
    public ITemplateResolver templateResolver() {
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        // ServletContextTemplateResolver需要一个ServletContext作为构造参数，可通过WebApplicationContext 的方法获得
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
                webApplicationContext.getServletContext());
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }

    // 2.2 生成模板引擎，注入模板解析器
    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { // 参数通过自动装配注入
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        return templateEngine;
    }

    // 2.3 生成视图解析器，注入解析器模板引擎
    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setCharacterEncoding("UTF-8");
        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
    }

    //3、视图控制器 view-controller
    @Override // 该功能在springmvc.xml中不是通过bean引入，而是配置文件中本身集成，所以要引入 implements WebMvcConfigurer
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/hello").setViewName("hello");
    }


    //4、开放对静态资源的访问 <mvc:default-servlet-handler />
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }


    //6、文件上传解析器
    @Bean
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        return commonsMultipartResolver;
    }

    //7、异常处理   既可以通过设置bean、也可以通过 implements WebMvcConfigurer 来实现。
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        // Properties 虽然继承了HashTable，但是 Properties 最重要的功能是用来操作属性集 Properties 文件，文件中存储的kv都是字符串类型，
        // 在Map中，put和set方法的参数是 put(Object key,Object value) ，都是 Object 类型。
        // 所以 Properties 在操作这种文件时没有put和get方法，只有对应的 setProperty 和 getProperty 方法。
        Properties prop = new Properties();
        // 针对算法异常，指定error.html页面
        prop.setProperty("java.lang.ArithmeticException", "error");
        exceptionResolver.setExceptionMappings(prop);
        // 设置异常信息共享在请求域中的，存放异常信息的键
        exceptionResolver.setExceptionAttribute("ex");
        resolvers.add(exceptionResolver);
    }
    //8、拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        TestInterceptor testInterceptor = new TestInterceptor();
        // 设置拦截器路径的匹配规则  可添加多个
        registry.addInterceptor(testInterceptor).addPathPatterns("/**");
    }

}
